/**
 * 把错误的数据转正
 * @example strip(0.09999999999999998)=0.1
 */
export function strip(num: number | string, precision = 15): number {
  return +parseFloat(Number(num).toPrecision(precision));
}

/**
 * 小数部分的长度
 */
export function digitLength(num: number | string) {
  const eSplit = num.toString().split(/[eE]/);
  if (eSplit.length > 0) {
    const len = (eSplit[0]!.split('.')[1] || '').length - Number(eSplit[1] || 0);
    return len > 0 ? len : 0;
  } else {
    return 0;
  }
}

/**
 * 把小数转成整数,如果是小数则放大成整数
 */
export function float2Fixed(num: number | string) {
  if (num.toString().indexOf('e') === -1) {
    return Number(num.toString().replace('.', ''));
  }
  const dLen = digitLength(num);
  return dLen > 0 ? strip(Number(num) * 10 ** dLen) : Number(num);
}

/**
 * 高精度乘法
 */
export function times(...nums: (number | string)[]) {
  if (nums.length > 2) {
    return _iteratorOperation(nums, times);
  }

  const [num1, num2] = nums;
  const num1Changed = float2Fixed(num1!);
  const num2Changed = float2Fixed(num2!);
  const baseNum = digitLength(num1!) + digitLength(num2!);
  const leftValue = num1Changed * num2Changed;
  return leftValue / 10 ** baseNum;
}

/**
 * 高精度加法
 */
export function plus(...nums: (number | string)[]) {
  if (nums.length > 2) {
    return _iteratorOperation(nums, plus);
  }

  const [num1, num2] = nums;
  // 取最大的小数位
  const baseNum = 10 ** Math.max(digitLength(num1!), digitLength(num2!));
  // 把小数都转为整数然后再计算
  return (times(num1!, baseNum) + times(num2!, baseNum)) / baseNum;
}

/**
 * 高精度减法
 */
export function minus(...nums: (number | string)[]) {
  if (nums.length > 2) {
    return _iteratorOperation(nums, minus);
  }

  const [num1, num2] = nums;
  const baseNum = 10 ** Math.max(digitLength(num1!), digitLength(num2!));
  return (times(num1!, baseNum) - times(num2!, baseNum)) / baseNum;
}

/**
 * 高精度除法
 * @param num - 要操作的数字
 */
export function divide(...nums: (number | string)[]) {
  if (nums.length > 2) {
    return _iteratorOperation(nums, divide);
  }

  const [num1, num2] = nums;
  const num1Changed = float2Fixed(num1!);
  const num2Changed = float2Fixed(num2!);
  // 重要，这里必须用strip进行修正
  return times(num1Changed / num2Changed, strip(10 ** (digitLength(num2!) - digitLength(num1!))));
}

/**
 * 四舍五入保留 n 位小数
 * @param num - 要操作的数字
 * @param ratio - 需要保留几位小数
 */
export function round(num: number | string, ratio: number) {
  const base = 10 ** ratio;
  let result = divide(Math.round(Math.abs(times(num, base))), base);
  if (Number(num) < 0 && result !== 0) {
    result = times(result, -1);
  }
  return result;
}

/**
 * 把递归操作扁平迭代化
 * @param arr - 要操作的数字数组
 * @param operation - 迭代操作
 * @private
 */
function _iteratorOperation(arr: (number | string)[], operation: (...ag: (number | string)[]) => number) {
  const [num1, num2, ...others] = arr;
  let res = operation(num1!, num2!);

  others.forEach((num) => {
    res = operation(res, num);
  });

  return res;
}
